package cn.tanghom.app.streamming.config;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Map;


@Component("streamView")
public class StreamView extends AbstractView {
    private static final Logger logger = LoggerFactory.getLogger(StreamView.class);
    
    public static final String VIDEO_ROOT;  //视频文件
    
    public static final String FILE_ROOT;  // 其他二进制文件

    static {
        final String path = System.getProperty("video.root","WebRoot/upload/video");
        final String file_path = System.getProperty("upload.root","WebRoot/upload");
        final String fileSeparator = System.getProperty("file.separator");
        VIDEO_ROOT = path + (path.matches("^.*[" + (fileSeparator.equals("\\") ? "\\\\" : fileSeparator) + "]$") ? "" : fileSeparator);
        FILE_ROOT = file_path + (file_path.matches("^.*[" + (fileSeparator.equals("\\") ? "\\\\" : fileSeparator) + "]$") ? "" : fileSeparator);
    }
    

    @Override
    protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest request, HttpServletResponse response) throws Exception {
        final File movieFIle = new File(VIDEO_ROOT + (String) map.get("movieName"));
        final RandomAccessFile randomFile = new RandomAccessFile(movieFIle, "r");

        long rangeStart = 0; //요청 범위의 시작 위치
        long rangeEnd = 0; //요청 범위의 끝 위치
        boolean isPart = false; //부분 요청일 경우 true, 전체 요청의 경우 false

        // randomFile 을 클로즈 하기 위하여 try~finally 사용
        try {
            long movieSize = randomFile.length();//동영상 파일 크기
            String range = request.getHeader("range");//스트림 요청 범위, request의 헤더에서 range를 읽는다.
            logger.debug("range: {}", range);

            //浏览器根据range类型不同，基本形式是“bytes = end start”形式。
            // range是null或者0，reqStart，end无完整的请求。
            //请求范围解围。
            if (range != null) {
                if (range.endsWith("-")) {//처리의 편의를 위해 요청 range에 end 값이 없을 경우 넣어줌</font>
                    range = range + (movieSize - 1);
                }
                int idxm = range.trim().indexOf("-");//"-" 위치
                rangeStart = Long.parseLong(range.substring(6, idxm));
                rangeEnd = Long.parseLong(range.substring(idxm + 1));
                if (rangeStart > 0) {
                    isPart = true;
                }
            } else {	// range是null的情况下视频的整体大小。初始值。从零开始，- 1
                rangeStart = 0;
                rangeEnd = movieSize - 1;
            }

            long partSize = rangeEnd - rangeStart + 1; //전송 파일 크기
            logger.debug("accepted range: {}", rangeStart + "-" + rangeEnd + "/" + partSize + " isPart:" + isPart);
            response.reset();//전송시작
            response.setStatus(isPart ? 206 : 200);// 전체 요청일 경우 200, 부분 요청일 경우 206을 반환상태 코드로 지정
            response.setContentType("video/mp4");// mime type 지정

            // 전송 내용을 헤드에 넣어준다. 마지막에 파일 전체 크기를 넣는다.
            response.setHeader("Content-Range", "bytes " + rangeStart + "-" + rangeEnd + "/" + movieSize);
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Content-Length", "" + partSize);

            OutputStream out = response.getOutputStream();
            randomFile.seek(rangeStart);//동영상 파일의 전송시작 위치 지정

            // 文件传输。java io的一次传送到int作为指定byte
            // 视频文件的处理是int型的大小的文件，
            // 8kb切成，大文件的大小也不成问题。
            int bufferSize = 8 * 1024;
            byte[] buf = new byte[bufferSize];
            do {
                int block = partSize > bufferSize ? bufferSize : (int) partSize;
                int len = randomFile.read(buf, 0, block);
                out.write(buf, 0, len);
                partSize -= block;
            } while (partSize > 0);
            logger.debug("sent " + movieFIle.getAbsolutePath() + " " + rangeStart + "-" + rangeEnd);
        } catch (IOException e) {
        	//传输中的浏览器关闭或关闭屏幕切换时，传输被取消。
        	//progressBar 点击点击的位置的情况下，用残灰厅来，传输被取消。
            logger.debug("传送被取消了.");
        } finally {
            randomFile.close();
        }
    }
}